package com.cygsunri.airCondition.task;

import com.cygsunri.airCondition.service.AirCacheService;
import com.cygsunri.common.constant.DataName;
import com.cygsunri.common.constant.DeviceType;
import com.cygsunri.common.service.CommonDataService;
import com.cygsunri.measurement.calculate.AbstractComplexEndureCalculateService;
import com.cygsunri.measurement.entity.MeasurementValue;
import com.cygsunri.scada.base.BasePSR;
import com.cygsunri.scada.service.ScadaMeasurementService;
import com.cygsunri.scada.service.ScadaPSRService;
import com.cygsunri.util.DateUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Component
@Slf4j
@ConditionalOnProperty(prefix = "task.air", name = "sub", havingValue = "true")
@PropertySource(name = "application.yml", value = {"classpath:application.yml"}, ignoreResourceNotFound = false, encoding = "UTF-8")
public class AirSubCalcTask extends AbstractComplexEndureCalculateService {

    @Autowired
    private ScadaPSRService scadaPSRService;

    @Autowired
    private ScadaMeasurementService scadaMeasurementService;

    @Autowired
    private CommonDataService commonDataService;

    @Autowired
    private AirCacheService airCacheService;

    private Map<BasePSR, List<BasePSR>> map = new HashMap<>();
    private Map<BasePSR, List<BasePSR>> outMap = new HashMap<>();

    @Bean
    public void airSubCalcInit() {
        List<BasePSR> subs = scadaPSRService.getDevicesByType(DeviceType.AIR_SUB.getKey());
        for (BasePSR sub : subs) {
            List<BasePSR> airInsides = scadaPSRService.getDeviceOfSubstationByType(sub.getId(), DeviceType.AIR_INSIDE.getKey());
            if (airInsides.isEmpty()) return;
            map.put(sub, airInsides);

            List<BasePSR> airOutsides = scadaPSRService.getDeviceOfSubstationByType(sub.getId(), DeviceType.AIR_OUTSIDE.getKey());
            if (airOutsides.isEmpty()) return;
            outMap.put(sub, airOutsides);
        }
    }

    @Scheduled(cron = "${task.air.cron}")
    private void airSubCalc() {
        long a = System.currentTimeMillis();
        map.forEach((sub, airInsides) -> {
            calcUtilization(sub, airInsides);
            calcTemperature(sub, airInsides);
        });
        outMap.forEach(this::airDeviceStatus);
        log.info("空调计算量共耗时：{} ms", System.currentTimeMillis() - a);
    }

    /**
     * 计算空调使用率
     */
    private void calcUtilization(BasePSR sub, List<BasePSR> airInsides) {
        if (!this.getNeedEndureCalculate(sub.getId(), sub.getName(), DataName.UTILIZATION)) {
            return;
        }

        ImmutablePair<String, Integer> pair = scadaMeasurementService.getMeasurementID(sub.getId(), DataName.UTILIZATION);
        if (pair == null) {
            log.warn("找不到id为{}的设备{}在scada中的{}点！", sub.getId(), sub.getName(), DataName.UTILIZATION);
            return;
        }

        Double utilization;
        Double use = 0d;
        Double num = 0d;
        for (BasePSR airInside : airInsides) {
            MeasurementValue m = commonDataService.getValue(airInside.getId(), DataName.STATUS);
            if (!m.isInValid()) {
                num++;
//                if (m.getData() == 9 || m.getData()  == 10 || m.getData() == 11 || m.getData() == 12) {
                if (m.getData() == 1 ) {
                    use++;
                }
            }
        }

        if (num > 0) {
            utilization = use / num;
            this.saveMean(pair.getLeft(), utilization * 100, DateUtil.nowMilliSeconds(), MeasurementValue.Quality.Calc.getKey());
        }
    }

    /**
     * 计算空调平均温度
     */
    private void calcTemperature(BasePSR sub, List<BasePSR> airInsides){
        if (!this.getNeedEndureCalculate(sub.getId(), sub.getName(), DataName.TEMPERATURE)) {
            return;
        }

        ImmutablePair<String, Integer> pair = scadaMeasurementService.getMeasurementID(sub.getId(), DataName.TEMPERATURE);
        if (pair == null) {
            log.warn("找不到id为{}的设备{}在scada中的{}点！", sub.getId(), sub.getName(), DataName.TEMPERATURE);
            return;
        }

        Double sum = 0d;
        Double num = 0d;
        for (BasePSR airInside : airInsides) {
            MeasurementValue status = commonDataService.getValue(airInside.getId(), DataName.STATUS);
            MeasurementValue m = commonDataService.getValue(airInside.getId(), DataName.TEMPERATURE);
            if(!status.isInValid()){
                if(status.getData() == 1 && !m.isInValid()){
                    num++;
                    sum += m.getData();
                }
            }
//            if (!m.isInValid()) {
//                num++;
//                sum += m.getData();
//            }
        }

        if (num > 0) {
            this.saveMean(pair.getLeft(), sum / num, DateUtil.nowMilliSeconds(), MeasurementValue.Quality.Calc.getKey());
        }
    }

    /**
     * 空调设备通讯状态
     */
    private void airDeviceStatus(BasePSR sub, List<BasePSR> airOutsides) {
        for (BasePSR airOutside : airOutsides) {
            MeasurementValue communicationFault = commonDataService.getValue(airOutside.getId(), DataName.COMMUNICATION_FAULT);
            if (!communicationFault.isInValid() && communicationFault.getData() == 1) {
                setDeviceStatus(airOutside, "COMMUNICATION_FAULT");
            } else {
                setDeviceStatus(airOutside, "RUNNING");
            }
        }
    }

    /**
     * 统计空调使用时间任务
     */
    @Scheduled(cron = "${task.air.workTime}")
    private void airTimeCalc() {
        long a = System.currentTimeMillis();
        String date = DateUtil.beforeDay(DateUtil.today());
        map.forEach((sub, list) ->
                airCacheService.calcWorkTime(date, sub, list)
        );
        log.info("日期：{}， 空调缓存耗时：{} ms", date, System.currentTimeMillis() - a);
    }
}
